using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace SpellCenter.Core
{
    sealed public class FilesInCorpusList
    {
        private Guid _id;
        private List<FilePattern> _includePatterns;
        private List<FilePattern> _excludePatterns;
        private string _writingSystemId;

        public FilesInCorpusList()
        {
            _id = Guid.NewGuid();
            _includePatterns = new List<FilePattern>();
            _excludePatterns = new List<FilePattern>();

            WritingSystemId = "und"; //ISO 639-3 for undetermined language
        }

        /// <summary>
        /// Deserialize the xml file and return the <code>FilesInCorpusList</code> object
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static FilesInCorpusList CreateFromFile(FileInfo name)
        {
            using (FileStream read = name.OpenRead())
            {
                FilesInCorpusList ret = (FilesInCorpusList)Serializer.Deserialize(read);
//                ret._filename = name.FullName;
                return ret;
            }
        }

      public static FilesInCorpusList CreateFromFile(string name)
      {
        return CreateFromFile(new FileInfo(name));
      }

        //public static FilesInCorpusList OpenWritingSystem(string ws)
        //{
        //    return Open(new FileInfo(Path.Combine(BaseConfigFilePath, ws)));
        //}

        /// <summary>
        /// Save this object to disk via serialization
        /// </summary>
        /// <param name="name"></param>
        public void SaveAs(FileInfo name)
        {
            if (name == null) throw new ArgumentNullException();
//            _filename = name.FullName;
            
            if (string.IsNullOrEmpty(WritingSystemId))
            {
                WritingSystemId = name.Name.Substring(0, name.Name.Length - name.Extension.Length);
            }
            File.WriteAllText(name.FullName, string.Empty);
            using (StreamWriter mainConfigFile = new StreamWriter(name.OpenWrite()))
            {
                Serializer.Serialize(mainConfigFile, this);
            }
        }
      public void SaveAs(string name)
      {
        SaveAs(new FileInfo(name));
      }
        ///// <summary>
        ///// Save this object to disk via serialization.  Uses Writing System as a filename and places
        ///// the file in the common application settings directory under SpellCenter.
        ///// </summary>
        //public void Save()
        //{
        //    if(string.IsNullOrEmpty(_writingSystemId))
        //        throw new InvalidOperationException("Can't save file when filename is null or empty");

        //    string fname = WritingSystemId.Replace('\\', '_').Replace('/', '_').Replace('?', '_').Replace('*', '_');
        //    FileInfo file = new FileInfo(Path.Combine(BaseConfigFilePath, fname));
        //    SaveAs(file);
        //}

        //private string _filename;
        ///// <summary>
        ///// The full path where this config file is saved
        ///// </summary>
        //public string Filename
        //{
        //    get { return _filename; }
        //    set { _filename = value; }
        //}

        private static XmlSerializer _serializer;

        private static XmlSerializer Serializer
        {
            get
            {
                if (_serializer == null)
                {
                    _serializer = new XmlSerializer(typeof(FilesInCorpusList));
                }
                return _serializer;
            }
        }

        public Guid Id
        {
            get { return _id; }
            set
            {
                if (value == Guid.Empty) throw new ArgumentException();
                _id = value;
            }
        }

        [XmlIgnore]
        public ICollection<string> Files
        {
          get
          {
            return GetIncludedFilesIfNotExcluded();
          }
        }

        private List<string> GetIncludedFilesIfNotExcluded()
        {
          List<string> includes = new List<string>();
          foreach (FilePattern pattern in IncludePatterns)
          {
            foreach (string file in pattern.GetMatches())
            {
              if(!IsExcluded(file, pattern))
              {
                // it is possible that a file might be matched by
                // more than one pattern, but we should only list it once
                if (!includes.Contains(file))
                {
                  includes.Add(file);
                }
              }
            }
          }

          return includes;
        }

      //A file should be excluded if the exclude rule is more
      // specific than or the same as the rule that included it in the first place
        private bool IsExcluded(string file, FilePattern inclusionPattern)
        {
          foreach (FilePattern pattern in ExcludePatterns)
          {
            if(pattern == inclusionPattern
               || (pattern.IsMoreSpecificThan(inclusionPattern)
                   && pattern.AppliesTo(file)))
            {
              return true;
            }
          }
          return false;
        }

        public List<FilePattern> IncludePatterns
        {
            get { return _includePatterns; }
            set
            {
                if (value == null) throw new ArgumentNullException();
                _includePatterns = value;
            }
        }

        public List<FilePattern> ExcludePatterns
        {
            get { return _excludePatterns; }
            set {
                if (value != null) throw new ArgumentNullException();
                _excludePatterns = value;
            }
        }

        public string WritingSystemId
        {
            get { return _writingSystemId;  }
            set
            {
                _writingSystemId = value;
            }
        }

        public void AddFile(string fileName)
        {
            AddFile(new FileInfo(fileName));
        }
        
        public void AddFile(FileInfo fileInfo)
        {
            FilePattern item = new FilePattern(fileInfo.DirectoryName, fileInfo.Name, SearchOption.TopDirectoryOnly);
            ExcludePatterns.Remove(item);

            if (!IncludePatterns.Contains(item))
            {
                IncludePatterns.Add(item);
            }
        }

      public void RemoveFile(string fileName)
      {
        RemoveFile(new FileInfo(fileName));
      }

      public void RemoveFile(FileInfo fileInfo)
      {
        FilePattern item = new FilePattern(fileInfo.DirectoryName, fileInfo.Name, SearchOption.TopDirectoryOnly);
        
        IncludePatterns.Remove(item);

        if (!ExcludePatterns.Contains(item))
        {
          //only add to exclude pattern if it would have been included
          //by an include pattern
          foreach(FilePattern pattern in IncludePatterns)
          {
            if (pattern.AppliesTo(fileInfo.FullName))
            {
              ExcludePatterns.Add(item);
              break;
            }
          }
        }
      }

      public void Clear()
      {
        IncludePatterns.Clear();
        ExcludePatterns.Clear();
      }
    }
}